package io.github.neo.hhcache.core;

import io.github.neo.hhcache.entry.Reply;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

import static io.github.neo.hhcache.core.Command.CRLF;
import static io.github.neo.hhcache.core.Command.OK;

/**
 * Description for this class.
 *
 * @Author : hh(hh@apache.org)
 * @create 2024/6/12 下午8:36
 */
public class HHCacheHandler extends SimpleChannelInboundHandler<String> {
//public class HHCacheHandler extends SimpleChannelInboundHandler<RedisMessage> {

    // 报文结束后的符号
    private static final String STR_PREFIX = "+";
    private static final String BULK_PREFIX = "$";


    // 默认实例
    public static final HHCache cache = new HHCache();


    @Override
    protected void channelRead0(ChannelHandlerContext ctx,
                                String message) throws Exception {

        String[] args = message.split(CRLF);
        System.out.println("HHCacheHandler => " + String.join(",", args));
        String cmd = args[2].toUpperCase();

        final Command command = Commands.get(cmd);
        if (command != null) {
            try {
                final Reply<?> reply = command.exec(cache, args);
                System.out.println("CMD[" + cmd + "] => " + reply.getType() + " => " + reply.getValue());
                replyContext(ctx, reply);
            } catch (Exception ex) {
                final Reply<String> reply = Reply.error("EXP exception with msg:  '" + ex.getMessage() + "'");
                replyContext(ctx, reply);
            }
        } else {
            final Reply<String> reply = Reply.error("ERR unsupported command [" + cmd + "]");
            replyContext(ctx, reply);
        }
    }

    private void replyContext(ChannelHandlerContext ctx, Reply<?> reply) {
        switch (reply.getType()) {
            case INT:
                integer(ctx, (Integer) reply.getValue());
                break;
            case ERROR:
                error(ctx, (String) reply.getValue());
                break;
            case BULK_STRING:
                bulkString(ctx, (String) reply.getValue());
                break;
            case SIMPLE_STRING:
                simpleString(ctx, (String) reply.getValue());
                break;
            case ARRAY:
                array(ctx, (String[]) reply.getValue());
                break;
            default:
                simpleString(ctx, OK);

        }
    }

    private void error(ChannelHandlerContext ctx, String msg) {
        writeByteBuf(ctx, errorEncode(msg));
    }

    private void array(ChannelHandlerContext ctx, String[] array) {
        writeByteBuf(ctx, arrayEncode(array));
    }


    private void integer(ChannelHandlerContext ctx, int i) {
        writeByteBuf(ctx, integerEncode(i));
    }

    private void bulkString(ChannelHandlerContext ctx, String content) {
        writeByteBuf(ctx, bulkStringEncode(content));
    }

    private void simpleString(ChannelHandlerContext ctx, String content) {
        writeByteBuf(ctx, simpleStringEncode(content));
    }

    private String errorEncode(String msg) {
        return "-" + msg + CRLF;
    }

    private static String arrayEncode(Object[] array) {
        final StringBuilder sb = new StringBuilder();
        if (null == array) {
            sb.append("*-1" + CRLF);
        } else if (array.length == 0) {
            sb.append("*0" + CRLF);
        } else {
            sb.append("*" + array.length + CRLF);
            for (int i = 0; i < array.length; i++) {
                final Object obj = array[i];
                if (null == obj) {
                    sb.append("$-1" + CRLF);
                } else {
                    if (obj instanceof Integer) {
                        sb.append(integerEncode((Integer) obj));
                    } else if (obj instanceof String) {
                        sb.append(bulkStringEncode((String) obj));
                    } else if (obj instanceof Object[] objs) {
                        sb.append(arrayEncode(objs));
                    }
                }
            }
        }
        return sb.toString();
    }

    private static String integerEncode(int i) {
        return ":" + i + CRLF;
    }

    private static String simpleStringEncode(String content) {
        String ret = null;
        if (null == content) {
            // 空数据返回长度为-1的数据
            ret = "$-1";
        } else if (content.isEmpty()) {
            // 空字符串返回长度为0的数据
            ret = "$0";
        } else {
            ret = STR_PREFIX + content;
        }
        return ret + CRLF;
    }

    private static String bulkStringEncode(String content) {
        String ret = null;
        if (null == content) {
            // 空数据返回长度为-1的数据
            ret = "$-1";
        } else if (content.isEmpty()) {
            // 空字符串返回长度为0的数据
            ret = "$0";
        } else {
            ret = BULK_PREFIX + content.getBytes().length + CRLF + content;
        }
        return ret + CRLF;
    }

    private void writeByteBuf(ChannelHandlerContext ctx, String content) {
        System.out.println("wrap byte buffer and reply: " + content);
        ByteBuf buffer = Unpooled.buffer(128);
        buffer.writeBytes(content.getBytes());
        ctx.writeAndFlush(buffer);
    }


    /**
     * set a 100
     * *3
     * $3
     * set
     * $1
     * a
     * $3
     * 100
     * <p>
     * get a
     * *2
     * $3
     * get
     * $1
     * a
     */

//    @Override
//    protected void channelRead0(ChannelHandlerContext channelHandlerContext, RedisMessage message) throws Exception {
//        if (message instanceof ArrayHeaderRedisMessage m) {
//            System.out.println("1 => " + m.length());
//        }
//
//        if (message instanceof BulkStringHeaderRedisMessage m) {
//            System.out.println("2 => " + m.bulkStringLength());
//        }
//
//        if (message instanceof DefaultBulkStringRedisContent m) {
//            final int count = m.content().readableBytes();
//            final byte[] bytes = new byte[count];
//            m.content().readBytes(bytes);
//            System.out.println("3 => " + new String(bytes));
//            channelHandlerContext.writeAndFlush("OK\r\n");
//        }
//    }
}

